package com.lin.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.lin.gulimall.common.constant.WareConstant;
import com.lin.gulimall.common.constant.WareOrderTaskConstant;
import com.lin.gulimall.common.exception.NoStockException;
import com.lin.gulimall.common.to.SkuHasStockVo;
import com.lin.gulimall.common.to.mq.StockDetailTo;
import com.lin.gulimall.common.to.mq.StockLockedTo;
import com.lin.gulimall.common.to.order.OrderTO;
import com.lin.gulimall.common.utils.Query;
import com.lin.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.lin.gulimall.ware.entity.WareOrderTaskEntity;
import com.lin.gulimall.ware.enume.OrderStatusEnum;
import com.lin.gulimall.ware.feign.OrderFeignService;
import com.lin.gulimall.ware.feign.ProductFeignService;
import com.lin.gulimall.ware.service.WareOrderTaskDetailService;
import com.lin.gulimall.ware.service.WareOrderTaskService;
import com.lin.gulimall.ware.to.WareSkuLockTO;
import com.lin.gulimall.ware.vo.OrderItemVo;
import lombok.Data;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lin.gulimall.common.utils.PageUtils;
import com.lin.gulimall.common.utils.R;

import com.lin.gulimall.ware.dao.WareSkuDao;
import com.lin.gulimall.ware.entity.WareSkuEntity;
import com.lin.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private WareOrderTaskService wareOrderTaskService;
    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private OrderFeignService orderFeignService;

    // 查询商品库存列表
    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        // skuId: 1  wareId: 2
        String skuId = (String) params.get("skuId");
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(skuId)) {
            wrapper.eq("sku_id", skuId);
        }

        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)) {
            wrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //1、判断 如果还没有这个库存记录 就新增
        List<WareSkuEntity> entityList = wareSkuDao.selectList(
                new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (entityList == null || entityList.size() == 0) {
            WareSkuEntity skuEntity = new WareSkuEntity();
            skuEntity.setSkuId(skuId);
            skuEntity.setWareId(wareId);
            skuEntity.setStock(skuNum);
            skuEntity.setStockLocked(0);
            //TODO 远程查询sku的名字
            // 1、自己catch异常 如果失败，整个事务无需回滚
            //TODO 还可以用什么办法让异常出现以后不回滚？高级
            try {
                R r = productFeignService.info(skuId);
                Map<String,Object> data = (Map<String, Object>) r.get("skuInfo");

                if (r.getCode() != 0) {
                    skuEntity.setSkuName((String) data.get("skuName"));
                }
            } catch (Exception e){
            }
            wareSkuDao.insert(skuEntity);
        } else {
            wareSkuDao.addStock(skuId,wareId,skuNum);
        }
    }

    @Override
    public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) {
        return skuIds.stream().map(item -> {
            Long count = this.baseMapper.getSkuStock(item);
            SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
            skuHasStockVo.setSkuId(item);
            skuHasStockVo.setHasStock(count == null? false : count > 0);
            return skuHasStockVo;
        }).collect(Collectors.toList());
    }

    /**
     * 为某个订单锁定库存
     * @param
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean orderLockStock(WareSkuLockTO lockTO) {
        // 保存库存工作单详情信息
        // 追溯
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(lockTO.getOrderSn());
        wareOrderTaskEntity.setCreateTime(new Date());
        wareOrderTaskService.save(wareOrderTaskEntity);

        //1、按照下单的收货地址，找到一个就近仓库，锁定库存
        //2、找到每个商品在哪个仓库都有库存
        List<OrderItemVo> locks = lockTO.getLocks();

        List<SkuWareHasStock> collect = locks.stream().map(item -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            stock.setSkuId(skuId);
            stock.setNum(item.getCount());
            //查询这个商品在哪个仓库有库存
            List<Long> wareIdList = wareSkuDao.listWareIdHasSkuStock(skuId);
            stock.setWareId(wareIdList);
            return stock;
        }).collect(Collectors.toList());

        //2、锁定库存
        for (SkuWareHasStock stock : collect) {
            boolean result = false;
            Long skuId = stock.getSkuId();
            List<Long> wareIds = stock.getWareId();

            if (StringUtils.isEmpty(wareIds)) {
                //没有任何仓库有这个商品的库存
                throw new NoStockException(skuId);
            }
            //1、如果每一个商品都锁定成功,将当前商品锁定了几件的工作单记录发给MQ
            //2、锁定失败。前面保存的工作单信息都回滚了。发送出去的消息，即使要解锁库存，由于在数据库查不到指定的id，所有就不用解锁
            for (Long wareId : wareIds) {
                //锁定成功就返回1，失败就返回0
                Long count = wareSkuDao.lockSkuStock(skuId,wareId,stock.getNum());
                if (count == 1) {
                    result = true;
                    R r = productFeignService.info(skuId);
                    Map<String,Object> data = (Map<String, Object>) r.get("skuInfo");
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
                    wareOrderTaskDetailEntity.setSkuId(skuId);
                    wareOrderTaskDetailEntity.setWareId(wareId);
                    wareOrderTaskDetailEntity.setSkuNum(stock.getNum());
                    wareOrderTaskDetailEntity.setLockStatus(WareOrderTaskConstant.LockStatusEnum.LOCKED.getCode());
                    wareOrderTaskDetailEntity.setSkuName((String) data.get("skuName"));
                    wareOrderTaskDetailEntity.setTaskId(wareOrderTaskEntity.getId());
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);

                    //TODO 告诉MQ库存锁定成功
                    StockLockedTo stockLockedTo = new StockLockedTo();
                    stockLockedTo.setId(wareOrderTaskEntity.getId());
                    StockDetailTo stockDetailTo = new StockDetailTo();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity, stockDetailTo);
                    stockLockedTo.setDetailTo(stockDetailTo);
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockedTo);
                    break;
                } else {
                    //当前仓库锁失败，重试下一个仓库
                }
            }
            if (result == false) {
                //当前商品所有仓库都没有锁住
                throw new NoStockException(skuId);
            }
        }
        return true;
    }

    @Override
    public void unLockStock(StockLockedTo locked) throws Exception {
        //库存工作单的id
        StockDetailTo detailTo = locked.getDetailTo();
        Long detailId = detailTo.getId();

        /**
         * 解锁
         * 1、查询数据库关于这个订单锁定库存信息
         *   有：证明库存锁定成功了
         *      解锁：订单状况
         *          1、没有这个订单，必须解锁库存
         *          2、有这个订单，不一定解锁库存
         *              订单状态：已取消：解锁库存
         *                      已支付：不能解锁库存
         */
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = wareOrderTaskDetailService.getById(detailId);
        if (wareOrderTaskDetailEntity != null) {
            // 1.工作单未回滚，需要解锁
            //查出wms_ware_order_task工作单的信息
            WareOrderTaskEntity taskEntity = wareOrderTaskService.getById(locked.getId());
            R r = orderFeignService.getOrderByOrderSn(taskEntity.getOrderSn());// 订单Entity
            if (r.getCode() == 0) {
                // 订单数据返回成功
                OrderTO order = r.getData(new TypeReference<OrderTO>() {});
                if (order == null || OrderStatusEnum.CANCLED.getCode().equals(order.getStatus())) {
                    // 2.订单已回滚 || 订单未回滚已取消状态
                    if (WareOrderTaskConstant.LockStatusEnum.LOCKED.equals(taskEntity.getTaskStatus())) {
                        // 订单已锁定状态，需要解锁（消息确认）
                        unLockStock(detailTo.getSkuId(), detailTo.getWareId(), detailTo.getSkuNum(),detailTo.getId());
                    } else {
                        // 订单其他状态，不可解锁（消息确认）
                    }
                }
            } else {
                // 订单远程调用失败（消息重新入队）
                throw new Exception();
            }
        } else {
            // 3.无库存锁定工作单记录，已回滚，无需解锁（消息确认）
        }
    }

    /**
     * 库存解锁
     * 订单解锁触发，防止库存解锁消息优先于订单解锁消息到期，导致库存无法解锁
     */
    @Transactional
    @Override
    public void unLockStock(OrderTO orderTo) throws Exception {
        String orderSn = orderTo.getOrderSn(); // 获取订单号
        // 1.根据订单号查询库存锁定工作单
        WareOrderTaskEntity task = wareOrderTaskService.getOrderTaskByOrderSn(orderSn);
        // 2.按照工作单查询未解锁的库存，进行解锁
        List<WareOrderTaskDetailEntity> wareOrderTaskDetailEntities = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                .eq("task_id", task.getId())
                .eq("lock_status", WareOrderTaskConstant.LockStatusEnum.LOCKED.getCode())); // 并发问题
        // 3.解锁库存
        for (WareOrderTaskDetailEntity taskDetail : wareOrderTaskDetailEntities) {
            unLockStock(taskDetail.getSkuId(), taskDetail.getWareId(), taskDetail.getSkuNum(), taskDetail.getId());
        }
    }

    /**
 * 库存解锁
 * 1.sql执行释放锁定
 * 2.更新库存工作单状态为已解锁
 * */
    private void unLockStock(Long skuId, Long wareId, Integer count, Long taskDetailId) {
        // 1.库存解锁
        baseMapper.unLockStock(skuId,wareId,count);

        // 2.更新工作单的状态 已解锁
        WareOrderTaskDetailEntity taskDetail = new WareOrderTaskDetailEntity();
        taskDetail.setId(taskDetailId);
        taskDetail.setLockStatus(WareOrderTaskConstant.LockStatusEnum.UNLOCKED.getCode());
        wareOrderTaskDetailService.updateById(taskDetail);
    }

    @Data
    class SkuWareHasStock {
        private Long skuId;
        private Integer num;
        private List<Long> wareId;
    }
}